package com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.VariableAPI.MathUtil.TimeUtil;
import com.nulldev.util.data.Arrays.maps.FastMaps;
import com.nulldev.util.data.Arrays.maps.sem.SelfExpiringHashMap;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.interper.BaseJSInterper;

public class BaseJSFunction extends BaseJSObject implements Cloneable {
	public final String[] arguments;
	public final String[] operations;

	public final Map<String, BaseJSVariable> valueCache = (Arguments.hasArgument("--BaseJS.enableGC")
			? new SelfExpiringHashMap<String, BaseJSVariable>(TimeUtil.fromMinutes(5))
			: FastMaps.map());
	public final AtomicReference<Object> returnValue = new AtomicReference<Object>();
	private static final Logger log = BaseJSInterp.log;

	public BaseJSFunction(final String name, final String[] arguments, final String[] operations) {
		super(name);
		this.arguments = arguments;
		this.operations = operations;
	}

	private volatile boolean debugFunction = Arguments.hasArgument("--BaseJSFunction.enableDebugging");

	public boolean isDebuggingFunction() {
		return this.debugFunction;
	}

	public BaseJSFunction setDebugging(final boolean value) {
		this.debugFunction = value;
		return this;
	}

	public Object run(final BaseJSEnvironment e, final Object... variables) {
		return this.run(e.isUsingNewInterpreter(), e, variables);
	}

	public Object run(final boolean USE_NEW_INTERPRETER, final BaseJSEnvironment e, final Object... variables) {
		BaseJSFunction self = null;
		try {
			self = this.clone();
		} catch (CloneNotSupportedException e1) {
			throw new RuntimeException(e1);
		}
		if (variables != null && variables.length > 0) {
			try {
				for (int i = 0; i < variables.length; i++) {
					self.valueCache.put(arguments[i], new BaseJSVariable(name, variables[i]));
				}
			} catch (Exception ef) {
				if (variables.length > arguments.length)
					log.trace(this.name + "() -> Argument count mismatch! [variables: " + variables.length + ", expected: " + arguments.length + "]");
				else {
					log.error("Failed to run: " + toString() + ", arguments: " + Arrays.toString(variables));
					throw new UnsupportedOperationException(
							"Failed to load function! [variables_expected: " + arguments.length + ", variables_given: " + variables.length + "]", ef);
				}
			}
		}
		if (this.debugFunction) {
			for (final String operation : operations) {
				final long start = System.currentTimeMillis();
				if (USE_NEW_INTERPRETER) {
					BaseJSInterper.interpretOperation(operation, self, e, variables);
				} else {
					BaseJSInterp.instance.interpOperation(operation, self, e, variables);
				}
				final long end = System.currentTimeMillis();
				log.info(operation);
				log.info("\tTime taken: " + (end - start) + "ms.");
			}
		} else {
			for (final String operation : operations) {
				if (USE_NEW_INTERPRETER) {
					BaseJSInterper.interpretOperation(operation, self, e, variables);
				} else {
					BaseJSInterp.instance.interpOperation(operation, self, e, variables);
				}
			}
		}
		self.valueCache.clear();
		return self.returnValue.get();
	}

	@Override
	protected BaseJSFunction clone() throws CloneNotSupportedException {
		return new BaseJSFunction(name, arguments, operations);
	}

	@Override
	public String toString() {
		return "BaseJSFunction[name: " + name + ", arguments: " + Arrays.toString(arguments) + "]";
	}

	public boolean hasObject(final String variable, final BaseJSEnvironment e) {
		if (valueCache.containsKey(variable))
			return true;
		if (e.contents.containsKey(variable))
			return true;
		return false;
	}

	public BaseJSVariable getObject(final String variable, final BaseJSEnvironment e) {
		if (valueCache.containsKey(variable))
			return valueCache.get(variable);
		if (e.contents.containsKey(variable))
			return new BaseJSVariable(variable, e.contents.get(variable));
		return null;
	}
}
